فارسی

یک راهنمای عملی برای ریفکتورینگ کدهای لگسی، شامل شناسایی، اولویت‌بندی، تکنیک‌ها و بهترین شیوه‌ها برای نوسازی و قابلیت نگهداری.

رام کردن هیولا: استراتژی‌های ریفکتورینگ برای کدهای لگسی

کد لگسی (Legacy code). خود این اصطلاح اغلب تصاویری از سیستم‌های گسترده و بدون مستندات، وابستگی‌های شکننده و حسی طاقت‌فرسا از ترس و وحشت را تداعی می‌کند. بسیاری از توسعه‌دهندگان در سراسر جهان با چالش نگهداری و توسعه این سیستم‌ها روبرو هستند که اغلب برای عملیات کسب‌وکار حیاتی هستند. این راهنمای جامع، استراتژی‌های عملی برای ریفکتورینگ کدهای لگسی ارائه می‌دهد و یک منبع ناامیدی را به فرصتی برای نوسازی و بهبود تبدیل می‌کند.

کد لگسی چیست؟

پیش از پرداختن به تکنیک‌های ریفکتورینگ، ضروری است که تعریف کنیم منظور ما از «کد لگسی» چیست. در حالی که این اصطلاح می‌تواند به سادگی به کدهای قدیمی اشاره داشته باشد، یک تعریف دقیق‌تر بر قابلیت نگهداری آن تمرکز دارد. مایکل فدرز (Michael Feathers) در کتاب برجسته خود «کار موثر با کدهای لگسی»، کد لگسی را به عنوان کدی بدون تست تعریف می‌کند. این فقدان تست باعث می‌شود که اصلاح ایمن کد بدون ایجاد رگرسیون (regression) دشوار باشد. با این حال، کد لگسی می‌تواند ویژگی‌های دیگری نیز داشته باشد:

مهم است توجه داشته باشید که کد لگسی ذاتاً بد نیست. این کد اغلب نمایانگر یک سرمایه‌گذاری قابل توجه است و دانش دامنه (domain knowledge) ارزشمندی را در خود جای داده است. هدف از ریفکتورینگ، حفظ این ارزش و در عین حال بهبود قابلیت نگهداری، قابلیت اطمینان و عملکرد کد است.

چرا کدهای لگسی را ریفکتور کنیم؟

ریفکتورینگ کدهای لگسی می‌تواند یک کار دلهره‌آور باشد، اما مزایای آن اغلب بر چالش‌ها غلبه می‌کند. در اینجا برخی از دلایل کلیدی برای سرمایه‌گذاری در ریفکتورینگ آورده شده است:

شناسایی کاندیداهای ریفکتورینگ

همه کدهای لگسی نیاز به ریفکتورینگ ندارند. مهم است که تلاش‌های ریفکتورینگ را بر اساس عوامل زیر اولویت‌بندی کنید:

مثال: یک شرکت لجستیک جهانی را با یک سیستم لگسی برای مدیریت محموله‌ها تصور کنید. ماژول مسئول محاسبه هزینه‌های حمل‌ونقل به دلیل تغییر مقررات و قیمت سوخت به طور مکرر به‌روزرسانی می‌شود. این ماژول یک کاندیدای اصلی برای ریفکتورینگ است.

تکنیک‌های ریفکتورینگ

تکنیک‌های ریفکتورینگ متعددی وجود دارد که هر کدام برای رسیدگی به بوهای کد خاص یا بهبود جنبه‌های خاصی از کد طراحی شده‌اند. در اینجا برخی از تکنیک‌های رایج آورده شده است:

ترکیب متدها (Composing Methods)

این تکنیک‌ها بر شکستن متدهای بزرگ و پیچیده به متدهای کوچک‌تر و قابل مدیریت‌تر تمرکز دارند. این کار خوانایی را بهبود می‌بخشد، تکرار را کاهش می‌دهد و تست کد را آسان‌تر می‌کند.

انتقال ویژگی‌ها بین اشیاء (Moving Features Between Objects)

این تکنیک‌ها بر بهبود طراحی کلاس‌ها و اشیاء با انتقال مسئولیت‌ها به جایی که به آن تعلق دارند، تمرکز می‌کنند.

سازماندهی داده‌ها (Organizing Data)

این تکنیک‌ها بر بهبود نحوه ذخیره و دسترسی به داده‌ها تمرکز دارند و درک و اصلاح آن را آسان‌تر می‌کنند.

ساده‌سازی عبارات شرطی (Simplifying Conditional Expressions)

منطق شرطی می‌تواند به سرعت پیچیده شود. این تکنیک‌ها با هدف شفاف‌سازی و ساده‌سازی انجام می‌شوند.

ساده‌سازی فراخوانی متدها (Simplifying Method Calls)

کار با تعمیم (Dealing with Generalization)

اینها تنها چند نمونه از تکنیک‌های ریفکتورینگ متعدد موجود هستند. انتخاب اینکه از کدام تکنیک استفاده شود به بوی کد خاص و نتیجه مطلوب بستگی دارد.

مثال: یک متد بزرگ در یک برنامه جاوا که توسط یک بانک جهانی استفاده می‌شود، نرخ‌های بهره را محاسبه می‌کند. اعمال استخراج متد (Extract Method) برای ایجاد متدهای کوچک‌تر و متمرکزتر، خوانایی را بهبود می‌بخشد و به‌روزرسانی منطق محاسبه نرخ بهره را بدون تأثیر بر سایر بخش‌های متد آسان‌تر می‌کند.

فرآیند ریفکتورینگ

ریفکتورینگ باید به صورت سیستماتیک انجام شود تا ریسک به حداقل برسد و شانس موفقیت به حداکثر برسد. در اینجا یک فرآیند پیشنهادی ارائه شده است:

  1. شناسایی کاندیداهای ریفکتورینگ: از معیارهای ذکر شده قبلی برای شناسایی مناطقی از کد که بیشترین سود را از ریفکتورینگ می‌برند، استفاده کنید.
  2. ایجاد تست‌ها: قبل از ایجاد هرگونه تغییر، تست‌های خودکار بنویسید تا رفتار موجود کد را تأیید کنید. این برای اطمینان از اینکه ریفکتورینگ رگرسیون ایجاد نمی‌کند، حیاتی است. ابزارهایی مانند JUnit (جاوا)، pytest (پایتون) یا Jest (جاوا اسکریپت) می‌توانند برای نوشتن تست‌های واحد استفاده شوند.
  3. ریفکتورینگ تدریجی: تغییرات کوچک و تدریجی ایجاد کنید و پس از هر تغییر تست‌ها را اجرا کنید. این کار شناسایی و رفع هرگونه خطایی که معرفی می‌شود را آسان‌تر می‌کند.
  4. کامیت مکرر: تغییرات خود را به طور مکرر در کنترل نسخه کامیت کنید. این به شما امکان می‌دهد در صورت بروز مشکل به راحتی به نسخه قبلی بازگردید.
  5. بررسی کد: کد خود را توسط یک توسعه‌دهنده دیگر بررسی کنید. این می‌تواند به شناسایی مشکلات بالقوه کمک کرده و اطمینان حاصل کند که ریفکتورینگ به درستی انجام شده است.
  6. نظارت بر عملکرد: پس از ریفکتورینگ، عملکرد سیستم را نظارت کنید تا اطمینان حاصل شود که تغییرات هیچ‌گونه رگرسیون عملکردی ایجاد نکرده‌اند.

مثال: تیمی که در حال ریفکتورینگ یک ماژول پایتون در یک پلتفرم تجارت الکترونیک جهانی است، از `pytest` برای ایجاد تست‌های واحد برای عملکرد موجود استفاده می‌کند. سپس آنها ریفکتورینگ استخراج کلاس (Extract Class) را برای جداسازی مسئولیت‌ها و بهبود ساختار ماژول اعمال می‌کنند. پس از هر تغییر کوچک، آنها تست‌ها را اجرا می‌کنند تا اطمینان حاصل کنند که عملکرد بدون تغییر باقی مانده است.

استراتژی‌هایی برای افزودن تست به کدهای لگسی

همانطور که مایکل فدرز به درستی بیان کرد، کد لگسی کدی بدون تست است. افزودن تست به پایگاه کدهای موجود می‌تواند یک کار عظیم به نظر برسد، اما برای ریفکتورینگ ایمن ضروری است. در اینجا چندین استراتژی برای انجام این کار آورده شده است:

تست‌های مشخصه‌یابی (Characterization Tests) (یا Golden Master Tests)

هنگامی که با کدی سر و کار دارید که درک آن دشوار است، تست‌های مشخصه‌یابی می‌توانند به شما کمک کنند تا رفتار موجود آن را قبل از شروع تغییرات ثبت کنید. ایده این است که تست‌هایی بنویسید که خروجی فعلی کد را برای مجموعه معینی از ورودی‌ها تأیید می‌کنند. این تست‌ها لزوماً صحت را تأیید نمی‌کنند؛ آنها به سادگی آنچه را که کد *در حال حاضر* انجام می‌دهد، مستند می‌کنند.

مراحل:

  1. یک واحد کد را که می‌خواهید مشخصه‌یابی کنید (مثلاً یک تابع یا متد) شناسایی کنید.
  2. مجموعه‌ای از مقادیر ورودی ایجاد کنید که طیفی از سناریوهای رایج و موارد خاص (edge-case) را نشان می‌دهند.
  3. کد را با آن ورودی‌ها اجرا کرده و خروجی‌های حاصل را ثبت کنید.
  4. تست‌هایی بنویسید که تأیید کنند کد دقیقاً آن خروجی‌ها را برای آن ورودی‌ها تولید می‌کند.

احتیاط: اگر منطق زیربنایی پیچیده یا وابسته به داده باشد، تست‌های مشخصه‌یابی می‌توانند شکننده باشند. آماده باشید تا در صورت نیاز به تغییر رفتار کد، آنها را به‌روزرسانی کنید.

متد جوانه (Sprout Method) و کلاس جوانه (Sprout Class)

این تکنیک‌ها که توسط مایکل فدرز نیز توصیف شده‌اند، با هدف افزودن قابلیت‌های جدید به یک سیستم لگسی و در عین حال به حداقل رساندن خطر شکستن کدهای موجود انجام می‌شوند.

متد جوانه: هنگامی که نیاز به افزودن یک ویژگی جدید دارید که مستلزم اصلاح یک متد موجود است، یک متد جدید ایجاد کنید که حاوی منطق جدید باشد. سپس، این متد جدید را از متد موجود فراخوانی کنید. این به شما امکان می‌دهد کد جدید را جدا کرده و آن را به طور مستقل تست کنید.

کلاس جوانه: مشابه متد جوانه، اما برای کلاس‌ها. یک کلاس جدید ایجاد کنید که قابلیت جدید را پیاده‌سازی می‌کند و سپس آن را در سیستم موجود ادغام کنید.

جعبه شنی (Sandboxing)

جعبه شنی شامل جداسازی کد لگسی از بقیه سیستم است و به شما امکان می‌دهد آن را در یک محیط کنترل شده تست کنید. این کار را می‌توان با ایجاد ماک‌ها (mocks) یا استاب‌ها (stubs) برای وابستگی‌ها یا با اجرای کد در یک ماشین مجازی انجام داد.

روش میکادو (The Mikado Method)

روش میکادو یک رویکرد حل مسئله بصری برای مقابله با وظایف پیچیده ریفکتورینگ است. این شامل ایجاد یک نمودار است که وابستگی‌های بین بخش‌های مختلف کد را نشان می‌دهد و سپس ریفکتورینگ کد به گونه‌ای که تأثیر آن بر سایر بخش‌های سیستم به حداقل برسد. اصل اصلی این است که تغییر را «امتحان کنید» و ببینید چه چیزی خراب می‌شود. اگر خراب شد، به آخرین وضعیت کاری بازگردید و مشکل را ثبت کنید. سپس قبل از تلاش مجدد برای تغییر اصلی، آن مشکل را برطرف کنید.

ابزارهای ریفکتورینگ

چندین ابزار می‌توانند به ریفکتورینگ کمک کنند، کارهای تکراری را خودکار کرده و در مورد بهترین شیوه‌ها راهنمایی ارائه دهند. این ابزارها اغلب در محیط‌های توسعه یکپارچه (IDE) ادغام شده‌اند:

مثال: یک تیم توسعه که روی یک برنامه #C برای یک شرکت بیمه جهانی کار می‌کند، از ابزارهای ریفکتورینگ داخلی Visual Studio برای تغییر نام خودکار متغیرها و استخراج متدها استفاده می‌کند. آنها همچنین از SonarQube برای شناسایی بوهای کد و آسیب‌پذیری‌های بالقوه استفاده می‌کنند.

چالش‌ها و ریسک‌ها

ریفکتورینگ کدهای لگسی بدون چالش و ریسک نیست:

بهترین شیوه‌ها

برای کاهش چالش‌ها و ریسک‌های مرتبط با ریفکتورینگ کدهای لگسی، این بهترین شیوه‌ها را دنبال کنید:

نتیجه‌گیری

ریفکتورینگ کدهای لگسی یک تلاش چالش‌برانگیز اما ارزشمند است. با دنبال کردن استراتژی‌ها و بهترین شیوه‌های ذکر شده در این راهنما، می‌توانید این هیولا را رام کرده و سیستم‌های لگسی خود را به دارایی‌های قابل نگهداری، قابل اعتماد و با عملکرد بالا تبدیل کنید. به یاد داشته باشید که به صورت سیستماتیک به ریفکتورینگ نزدیک شوید، به طور مکرر تست کنید و به طور موثر با تیم خود ارتباط برقرار کنید. با برنامه‌ریزی و اجرای دقیق، می‌توانید پتانسیل پنهان در کدهای لگسی خود را آزاد کرده و راه را برای نوآوری‌های آینده هموار کنید.